package com.keith.jpa.tenant.aop;

import com.keith.jpa.tenant.TenantJpaInspector;
import com.keith.jpa.tenant.context.TenantContextHolder;
import com.keith.jpa.tenant.context.UserTenant;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * TenantJpaAspect.java
 * 注解处理
 *
 * @author czykeih
 * @version 1.0 2023/10/25
 */
@Component
@Slf4j
@Aspect
@ConditionalOnProperty(prefix = "spring.jpa.tenant", name = "enable", havingValue = "true")
public class TenantJpaAspect implements Ordered {
    @PostConstruct
    public void init() {
        log.info("初始化tenant拦截器");
    }

    private final TenantJpaInspector tenantJpaInspector = new TenantJpaInspector();

    @Around("@annotation(com.keith.jpa.tenant.aop.JpaTenant)||@within(com.keith.jpa.tenant.aop.JpaTenant)")
    public Object tenant(ProceedingJoinPoint point) {
        Object result = null;
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        JpaTenant jpaTenant = AnnotationUtils.findAnnotation(methodSignature.getMethod(), JpaTenant.class);
        if (jpaTenant == null) {
            jpaTenant = AnnotationUtils.findAnnotation(methodSignature.getMethod().getDeclaringClass(), JpaTenant.class);
        }
        if (jpaTenant != null) {
            TenantContextHolder.setTenant(UserTenant.builder().tenant(jpaTenant.tenant()).penetration(jpaTenant.penetration()).tenantId(jpaTenant.tenantId()).build());
        }
        try {
            result = point.proceed();
        } catch (Throwable throwable) {
            log.error("JpaTenant注解解析执行异常:", throwable);
        } finally {
            if (jpaTenant != null) {
                TenantContextHolder.removeTenant();
            }
        }
        return result;
    }

    @Around("execution(* org.springframework.jdbc.core.JdbcOperations.*(..))")
    public Object interceptJdbcTemplateMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        if (args == null) {
            return joinPoint.proceed();
        }
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg instanceof String) {
                args[i] = tenantJpaInspector.inspect(String.valueOf(arg));
                break;
            }
        }
        return joinPoint.proceed(args);
    }

    @Override
    public int getOrder() {
        return -20;
    }
}
